<?php
/**
 * OpenID 2.0 Authentication for PHP
 *
 * PHP version 5
 *
 * LICENSE:
 *
 * Copyright (c) 2006-2007 Pádraic Brady <padraic.brady@yahoo.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *    * The name of the author may not be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @category    Authentication
 * @package     OpenID
 * @author      Pádraic Brady <padraic.brady@yahoo.com>
 * @license     http://opensource.org/licenses/bsd-license.php New BSD License
 * @version     $Id: Association.php 57 2007-07-17 20:03:27Z padraic $
 * @link        http://
 */

/** OpenID_Response_Exception */
require_once 'OpenID/Response/Exception.php';

/** OpenID_KVContainer */
require_once 'OpenID/KVContainer.php';

/**
 * OpenID Authentication 2.0 implementation in PHP5.
 *
 * @category   Zend
 * @package    OpenID
 * @copyright  Copyright (c) 2007 Pádraic Brady (http://blog.astrumfutura.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class OpenID_Response_Association
{

    /**
     * KVContainer object for this instance used to transform response
     * data into a transfer object capable of parsing and emitting
     * Key:Value formatted data strings
     *
     * @var OpenID_KVContainer
     */
    protected $_kv = null;

    /**
     * Is this response an error?
     *
     * @var boolean
     */
    protected $_isError = false;

    /**
     * Constructor
     *
     * @param HTTP_Request $response
     */
    public function __construct(HTTP_Request $request)
    {
        $this->setRequest($request);
    }

    /**
     * Parse the resulting Response object's body into a more
     * useful Key:Value format captured by OpenID_KVContainer
     *
     * @param HTTP_Request $response
     * @return void
     */
    public function setRequest(HTTP_Request $request)
    {
        try {
            $kv = new OpenID_KVContainer($request->getResponseBody());
        } catch (OpenID_Exception $e) {
            throw new OpenID_Response_Exception($e->getMessage(), $e->getCode());
        }
        if (isset($kv->ns) && !in_array($kv->ns, array(OpenID::OPENID_2_0_NAMESPACE))) {
            if (in_array($kv->ns, array(OpenID::OPENID_1_1_NAMESPACE, OpenID::OPENID_1_0_NAMESPACE))) {
                OpenID::setVersion(1.1);
            } else {
                // Invalid response, but may not yet be implemented by all 2.0 Providers
            }
        }
        if (isset($kv->error)) {
            $this->_isError = true;
            $this->_kv = $kv;
            return;
        }
        if (!$this->_isValidEncryptedResponse($kv) && !$this->_isValidUnencryptedResponse($kv)) {
            throw new OpenID_Response_Exception('Invalid association response received');
        }
        $this->_kv = $kv;
    }

    /**
     * Return the current KVContainer object
     *
     * @return OpenID_KVContainer
     */
    public function getKV()
    {
        return $this->_kv;
    }

    /**
     * Public method to determine if this response container error data
     *
     * @return boolean
     */
    public function isError()
    {
        return $this->_isError;
    }

    /**
     * Validation method to ensure response data isn't malformed or poses
     * a potential security risk.
     *
     * @param OpenID_KVContainer
     * @return boolean
     */
    protected function _isValidEncryptedResponse(OpenID_KVContainer $kv)
    {
        if (!isset($kv->assoc_handle, $kv->assoc_type, $kv->dh_server_public, $kv->enc_mac_key, $kv->expires_in, $kv->session_type)) {
            return false;
        }
        // if (!preg_match("/^\{HMAC-SHA(1|256)\}\{[A-za-z0-9]+\}\{[A-za-z0-9\/\+]+[=]{0,2}\}$/", $kv->assoc_handle)) {
            // return false;
        // }
        // elseif (!preg_match("/^HMAC-SHA(1|256)$/", $kv->assoc_type)) {
        if (!preg_match("/^HMAC-SHA(1|256)$/", $kv->assoc_type)) {
            return false;
        }
        elseif (!preg_match("/^[A-za-z0-9\/\+]+[=]{0,2}$/", $kv->enc_mac_key)) {
            return false;
        }
        elseif (!preg_match("/^\d+$/", $kv->expires_in)) {
            return false;
        }
        elseif (!preg_match("/^DH-SHA(1|256)$/", $kv->session_type)) {
            return false;
        }
        return true;
    }

    /**
     * Validation method to ensure response data isn't malformed or poses
     * a potential security risk.
     *
     * @param OpenID_KVContainer
     * @return boolean
     */
    protected function _isValidUnencryptedResponse(OpenID_KVContainer $kv)
    {
        if (!isset($kv->assoc_handle, $kv->assoc_type, $kv->mac_key, $kv->expires_in)) {
            return false;
        }
        if (!preg_match("/^\{HMAC-SHA(1|256)\}\{[A-za-z0-9]+\}\{[A-za-z0-9\/\+]+[=]{0,2}\}$/", $kv->assoc_handle)) {
            return false;
        }
        elseif (!preg_match("/^HMAC-SHA(1|256)$/", $kv->assoc_type)) {
            return false;
        }
        elseif (!preg_match("/^[A-za-z0-9\/\+]+[=]{0,2}$/", $kv->mac_key)) {
            return false;
        }
        elseif (!preg_match("/^\d+$/", $kv->expires_in)) {
            return false;
        }
        return true;
    }

}
